﻿using BootyBay.StoreAppMgmtCore;

using BootyBay.StoreAppMgmtCore.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MgmtSvcRef = BootyBay.StoreAppMgmtCore.AppMgmtServicesReference;

namespace BootyBay.StoreAppMgmtCore
{
    public class AgentServiceSession : AgentSessionBase
    {

        const int msAgentQueueRecurInterval = 5000;
        const int msServerQueueRecurInterval = 10000;

        private string clientUserName { get; set; }

        private ServiceHost svcHost;

        private StoreAppMgmtTaskRepository TaskRepo
        {
            get { return StoreAppMgmtTaskRepository.Instance(); }
           
        }

        public AgentServiceSession(string userName)
        {
            this.clientUserName = userName; 
            
        }

        private void StartServiceHost()
        {
            svcHost = new ServiceHost(typeof(AgentSvc));

            svcHost.Open();

            string baseAddressListString = "";
            foreach (var add in svcHost.BaseAddresses)
            {
                baseAddressListString += add.ToString() + " ";
            }

            PrintMsg(string.Format("The service is ready at {0}", baseAddressListString));


            // Close the ServiceHost.


        }

        public void Initialize()
        {
            PrintMsg(string.Format("Private Store Client Agent is initilizing for user \"{0}\"...", this.clientUserName));
            resetEventForAgentMonitorSessionAbort = new AutoResetEvent(false);
            resetEventForServerMonitorSessionAbort = new AutoResetEvent(false);
            TaskRepo.ClientUserName = this.clientUserName;
            StartServiceHost();
          
        }

        public async Task StartMonitoringAppMgmtTaskAsync()
        {
            //PrintMsg("Start monitor session...");
            PrintMsg("Retriving app mgmt tasks in queue for user \"" + this.clientUserName + "\"...");

            while (!isAborted)
            {                
               
                var queuedTask = TaskRepo.GetAllAppMgmtTasks(this.clientUserName, Models.AppOperationStatus.RequestSubmitted);
 
                foreach (var task in queuedTask)
                {
                    TaskRepo.ProcessAppMgmtTask(task, this.PrintMsg);
                }

                await Task.Delay(msAgentQueueRecurInterval);          

            }

            resetEventForAgentMonitorSessionAbort.Set();

        }

        private List<int> pulledAppOptRecordsList = new List<int>();

        public async Task StartMonitoringServerRequestedAppMgmtTaskAsync()
        {
            PrintMsg("Retriving app mgmt tasks requested from the server for the user \"" + this.clientUserName + "\"...");

            AppMgmtServicesReference.AppMgmtServicesClient cloudSvcClient = new AppMgmtServicesReference.AppMgmtServicesClient();


            while (!isAborted)
            {
                var allRecords = (await cloudSvcClient.GetLastOptRecordListForAllAppsAsync(this.clientUserName)).ToList();


                allRecords = allRecords.Where(r => r.Id_AppOptRecord > 0 && !pulledAppOptRecordsList.Contains(r.Id_AppOptRecord)).ToList();

                //currently just proceed uninstall requests.
                var allUninstallRequests = allRecords.Where(r => r.OperationType == AppMgmtServicesReference.AppOperationType.RequestToUninstall).ToList();

                if (allUninstallRequests.Count > 0)
                {
                    PrintMsg("Uninstallation Requests pulled.");
                    foreach (var task in allUninstallRequests)
                    {
                        PrintMsg("App to delete: " + task.ApplicationName);

                        pulledAppOptRecordsList.Add(task.Id_AppOptRecord);

                        TaskRepo.SubmitAppMgmtTask(new StoreAppMgmtTask()
                        {
                            AppInfoID = task.Id_Application,
                            AppPackageName = task.MajorVersionPackageName,
                            AppPublisherName = task.MajorVersionPublisherName,
                            OptType = AppOperationType.Uninstall,
                            UserName = task.UserName
                        });
                    }
                }


                 

                var allInstallRequests = allRecords.Where(r => r.OperationType == AppMgmtServicesReference.AppOperationType.RequestToInstall).ToList();

                if (allInstallRequests.Count > 0)
                {
                    PrintMsg("Installation requests pulled.");
                    foreach (var task in allInstallRequests)
                    {
                        PrintMsg("App to Install: " + task.ApplicationName);

                        pulledAppOptRecordsList.Add(task.Id_AppOptRecord);

                        var selectedProcessorArch = ExtractAppProcArchBasedOnOSArch(task.AppProcessorArchitectureList);

                        if (selectedProcessorArch != null)
                        {

                            TaskRepo.SubmitAppMgmtTask(new StoreAppMgmtTask()
                            {
                                AppInfoID = task.Id_Application,
                                AppPackageName = task.MajorVersionPackageName,
                                AppPublisherName = task.MajorVersionPublisherName,
                                OptType = AppOperationType.Install,
                                UserName = task.UserName,
                                PackageCertLocationUrl = selectedProcessorArch.PackageCertLocationUrl,
                                PackageLocationUrl = selectedProcessorArch.PackageLocationUrl,
                            });
                        }
                    }
                }

                // auto update
                var allInstalledItems = allRecords.Where(r => r.OperationType == AppMgmtServicesReference.AppOperationType.Install || r.OperationType == AppMgmtServicesReference.AppOperationType.Update).ToList();

                foreach (var task in allInstalledItems)
                {
                    pulledAppOptRecordsList.Add(task.Id_AppOptRecord);

                    var localStatus = TaskRepo.GetAppLocalStatus(new StoreAppMgmtTask()
                    {
                        AppPackageName = task.MajorVersionPackageName,
                        AppPublisherName = task.MajorVersionPublisherName,
                        AppVersion = task.MajorVersion
                    });

                    if (localStatus == AppLocalStatus.OutDated)
                    {
                        var selectedProcessorArch = ExtractAppProcArchBasedOnOSArch(task.AppProcessorArchitectureList);

                        if (selectedProcessorArch != null)
                        {
                            PrintMsg("Auto update for app requested: " + task.ApplicationName);

                            TaskRepo.SubmitAppMgmtTask(new StoreAppMgmtTask()
                            {
                                AppPackageName = task.MajorVersionPackageName,
                                AppPublisherName = task.MajorVersionPublisherName,
                                OptType = AppOperationType.Update,
                                UserName = task.UserName,
                                PackageCertLocationUrl = selectedProcessorArch.PackageCertLocationUrl,
                                PackageLocationUrl = selectedProcessorArch.PackageLocationUrl,
                            });
                        }

                    }

                }

                await Task.Delay(msServerQueueRecurInterval);    

            }

            resetEventForServerMonitorSessionAbort.Set();

        }

        void appOperationMgr_OnNotificationOutput(object sender, AppMgmtEventArgs args)
        {
            PrintMsg(args.EventMessage);
        }

        private bool isAborted;
        private AutoResetEvent resetEventForAgentMonitorSessionAbort;
        private AutoResetEvent resetEventForServerMonitorSessionAbort;


        public void AbortSession()
        {
            PrintMsg("Stopping monitor session...");

            svcHost.Close();             

            isAborted = true;

            resetEventForAgentMonitorSessionAbort.WaitOne(TimeSpan.FromSeconds(60));
            resetEventForServerMonitorSessionAbort.WaitOne(TimeSpan.FromSeconds(60));

            PrintMsg("Aborted.");

            resetEventForAgentMonitorSessionAbort.Close();
        }

        private MgmtSvcRef.ApplicationProcessorArchitecture ExtractAppProcArchBasedOnOSArch(MgmtSvcRef.ApplicationProcessorArchitecture[] list)
        {
            var neutualArch = list.Where(a => a.ProcessorArchitecture == (int)ProcessorArchitecture.Neutual).FirstOrDefault();
            if (neutualArch != null)
                return neutualArch;

            if (System.Environment.Is64BitOperatingSystem)
            {
                var x64Arch = list.Where(a => a.ProcessorArchitecture == (int)ProcessorArchitecture.x64).FirstOrDefault();
                return x64Arch;
            }
            else
            {
                var x86Arch = list.Where(a => a.ProcessorArchitecture == (int)ProcessorArchitecture.x86).FirstOrDefault();
                return x86Arch;
            }

            //may return null. invalid.
        }

    }
}
